use log;
use shlex;
use std::net::{Ipv4Addr, SocketAddrV4};

use crate::settings::DEFAULT_LISTEN_PORT;

pub trait NetInterface {
    fn add_fou(&self, port: u16);
    fn remove_fou(&self, port: u16);
    fn add_bridge(&self, name: &str);
    fn remove_bridge(&self, name: &str);
    fn add_gretap(&self, name: &str, key: u32, local_addr: SocketAddrV4, remote_addr: SocketAddrV4);
    fn remove_gretap(&self, name: &str);
    fn update_gretap(
        &self,
        name: &str,
        key: u32,
        local_addr: SocketAddrV4,
        remote_addr: SocketAddrV4,
    );
    fn link_up(&self, name: &str);
    fn link_down(&self, name: &str);
    fn add_ip(&self, name: &str, ip: Ipv4Addr);
}

pub struct SimpleNetInterface;

impl NetInterface for SimpleNetInterface {
    fn add_fou(&self, port: u16) {
        SimpleNetInterface::run_command(format!("ip fou add port {port} ipproto 47"));
    }

    fn remove_fou(&self, port: u16) {
        SimpleNetInterface::run_command(format!("ip fou remove port {port}"));
    }

    fn add_gretap(
        &self,
        name: &str,
        key: u32,
        local_addr: SocketAddrV4,
        remote_addr: SocketAddrV4,
    ) {
        let local = local_addr.ip();
        let remote = remote_addr.ip();
        let encap_sport = local_addr.port();
        let encap_dport = remote_addr.port();
        SimpleNetInterface::run_command(format!(
            "ip link add name {name} type gretap key {key} remote {remote} local {local} \
            encap fou encap-sport {encap_sport} encap-dport {encap_dport}"
        ));
    }

    fn remove_gretap(&self, name: &str) {
        SimpleNetInterface::run_command(format!("ip link del {name}"));
    }

    fn update_gretap(
        &self,
        name: &str,
        key: u32,
        local_addr: SocketAddrV4,
        remote_addr: SocketAddrV4,
    ) {
        let local = local_addr.ip();
        let remote = remote_addr.ip();
        let encap_sport = local_addr.port();
        let encap_dport = remote_addr.port();
        SimpleNetInterface::run_command(format!(
            "ip link set {name} type gretap key {key} remote {remote} local {local} \
            encap fou encap-sport {encap_sport} encap-dport {encap_dport}"
        ));
    }

    fn link_up(&self, name: &str) {
        SimpleNetInterface::run_command(format!("ip link set {} up", name,));
    }
    fn link_down(&self, name: &str) {
        SimpleNetInterface::run_command(format!("ip link set {} down", name,));
    }

    fn add_bridge(&self, name: &str) {
        SimpleNetInterface::run_command(format!("ip link add name {name} type bridge"));
    }

    fn remove_bridge(&self, name: &str) {
        SimpleNetInterface::run_command(format!("ip link del {name}"));
    }

    fn add_ip(&self, name: &str, ip: Ipv4Addr) {
        SimpleNetInterface::run_command(format!("ip addr add {ip}/24 dev {name}"));
    }
}

impl SimpleNetInterface {
    fn run_command(cmd: String) {
        match shlex::split(&cmd) {
            Some(vec) if vec.len() > 0 => {
                let output = std::process::Command::new(&vec[0]).args(&vec[1..]).output();
                match output {
                    Ok(_) => (),
                    Err(e) => log::error!("failed to run cmd [{}] error: {}", cmd, e),
                }
            }
            _ => {
                log::error!("failed to run cmd [{}]", cmd);
            }
        }
    }
}

const BRIDGE_NAME: &'static str = "vnetd_br0";

pub fn init_bridge(port: u16, vip: Ipv4Addr) {
    let ifce = SimpleNetInterface;
    ifce.add_fou(port);
    ifce.remove_bridge(BRIDGE_NAME);
    ifce.add_bridge(BRIDGE_NAME);
    ifce.add_ip(BRIDGE_NAME, vip);
    ifce.link_up(BRIDGE_NAME);
}
